#include <modules\graphics\opengl\opengl_texture.h>

namespace DelGE
{
	namespace Modules
	{
		namespace Graphics
		{
			OpenGLTexture::OpenGLTexture(string sFilepath, 
														 const Color& oMaskColor, 
														 GraphicsTexture_UVWrapMode eUWrapMode, 
														 GraphicsTexture_UVWrapMode eVWrapMode)
			{
				GLfloat glfUWrapMode, glfVWrapMode;
				FREE_IMAGE_FORMAT eFreeImageFormat;
				FIBITMAP* pData;
				FIBITMAP* pConvertedData;
				BYTE* pTextureData;

				eFreeImageFormat = FreeImage_GetFileType(sFilepath.c_str(), 0);
				if (eFreeImageFormat == FIF_UNKNOWN)
					throw DelGEError("Unknown image format");

				pData = FreeImage_Load(eFreeImageFormat, sFilepath.c_str(), 0);
				if (pData == NULL)
					throw DelGEError("Could not load texture \"" + sFilepath + "\"");

				FreeImage_FlipVertical(pData);
				pConvertedData = FreeImage_ConvertTo32Bits(pData);

				FreeImage_Unload(pData);

				if (pData != NULL)
				{
					this->iWidth = FreeImage_GetWidth(pConvertedData);
					this->iHeight = FreeImage_GetHeight(pConvertedData);
					this->iWidthPow2 = MathHelper::NextPowerOf2(this->iWidth);
					this->iHeightPow2 = MathHelper::NextPowerOf2(this->iHeight);
					this->fUOffset = (float)this->iWidth / (float)this->iWidthPow2;
					this->fVOffset = (float)this->iHeight / (float)this->iHeightPow2;

					pTextureData = new BYTE [this->iWidthPow2 * this->iHeightPow2 * 4];
					FreeImage_ConvertToRawBits(pTextureData, pConvertedData, this->iWidthPow2 * 4, 32, 0, 0, 0);
					
					int iLinesize = this->iWidthPow2 * 4;

					// flip colors from BGRA to RGBA
					for(int y = 0; y < this->iHeightPow2; y++)
					{
						int iYPosition = y * iLinesize;
						for(int x = 0; x < this->iWidthPow2; x++)
						{
							int iXPosition = (y * iLinesize) + x * 4;
							unsigned char ucTemp = pTextureData[iXPosition];
							pTextureData[iXPosition] = pTextureData[iXPosition + 2];
							pTextureData[iXPosition + 2] = ucTemp;

							if (oMaskColor[0] != -1)
							{
								if (pTextureData[iXPosition] == oMaskColor[0] && 
									pTextureData[iXPosition + 1] == oMaskColor[1] && 
									pTextureData[iXPosition + 2] == oMaskColor[2])
								{ pTextureData[iXPosition] = pTextureData[iXPosition + 1] = pTextureData[iXPosition + 2] = pTextureData[iXPosition + 3] = 0; }
									

								if ((x >= this->iWidth) ||
									(y >= this->iHeight))
									pTextureData[iXPosition + 3] = 0;
							}
						}	
					}
					
					glGenTextures(1, &this->uiTextureID);
					glBindTexture(GL_TEXTURE_2D, this->uiTextureID);
					
					glfUWrapMode = glfVWrapMode = GL_CLAMP_TO_EDGE;
					
					if (eUWrapMode == GraphicsTexture_UVWrapMode_Repeat)
						glfUWrapMode = GL_REPEAT;
					if (eVWrapMode == GraphicsTexture_UVWrapMode_Repeat)
						glfVWrapMode = GL_REPEAT;
					
					glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glfUWrapMode);
					glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glfVWrapMode);
					glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
					glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

					glTexImage2D(GL_TEXTURE_2D, 0, 4, this->iWidthPow2, this->iHeightPow2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pTextureData);
					
					FreeImage_Unload(pConvertedData);
					delete [] pTextureData;
					
					glBindTexture(GL_TEXTURE_2D, 0);
				}
				else
				{
					this->uiTextureID						= 0;
					this->iWidth = this->iHeight			= 0;
					this->iWidthPow2 = this->iHeightPow2	= 0;
					this->fUOffset = this->fVOffset			= 0;
				}
			}

			OpenGLTexture::OpenGLTexture(int iWidth, int iHeight,
														 GraphicsTexture_UVWrapMode eUWrapMode, 
														 GraphicsTexture_UVWrapMode eVWrapMode)
			{
				GLfloat glfUWrapMode, glfVWrapMode;
				BYTE* pTextureData;

				this->iWidth = iWidth;
				this->iHeight = iHeight;
				this->iWidthPow2 = MathHelper::NextPowerOf2(this->iWidth);
				this->iHeightPow2 = MathHelper::NextPowerOf2(this->iHeight);
				this->fUOffset = (float)this->iWidth / (float)this->iWidthPow2;
				this->fVOffset = (float)this->iHeight / (float)this->iHeightPow2;

				glfUWrapMode = glfVWrapMode = GL_CLAMP_TO_EDGE;
					
				if (eUWrapMode == GraphicsTexture_UVWrapMode_Repeat)
					glfUWrapMode = GL_REPEAT;
				if (eVWrapMode == GraphicsTexture_UVWrapMode_Repeat)
					glfVWrapMode = GL_REPEAT;

				pTextureData = new unsigned char [this->iWidthPow2 * this->iHeightPow2 * 4];

				glGenTextures(1, &this->uiTextureID);
				glBindTexture(GL_TEXTURE_2D, this->uiTextureID);

				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glfUWrapMode);
				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glfVWrapMode);
				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

				glTexImage2D(GL_TEXTURE_2D, 0, 4, this->iWidthPow2, this->iHeightPow2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pTextureData);
					
				glBindTexture(GL_TEXTURE_2D, 0);

				delete [] pTextureData;
			}

			OpenGLTexture::~OpenGLTexture()
			{
				glDeleteTextures(1, &this->uiTextureID);
			}

			unsigned int OpenGLTexture::GetGLTextureID() const
			{
				return this->uiTextureID;
			}
		}
	}
}